#include "raycaster.h"

RayCaster::RayCaster()
    : m_grid_eye_pos(0.0,0.0, 4.0, 1.0),	// SHOULDNT CHANGE
      m_light_pos(0.0,0.0,10.0, 1.0),
      m_eye_world_pos(0.0, 0.0, 20.0, 1.0),	// FOR WORLD TRANSLATION, CHANGE THIS
      local_x(1.0, 0.0, 0.0, 0.0),
      local_y(0.0, 1.0, 0.0, 0.0),
      local_z(0.0, 0.0, 1.0, 0.0),
      m_bgcolor(0.0, 0.0, 0.0)
{



}


QColor RayCaster::phong_lighting(glm::vec4 point, glm::vec4 normal, Material *material){
    glm::vec4 light_dir = glm::normalize(m_light_pos - point);
    glm::vec4 view_dir = glm::normalize(m_eye_world_pos - point);
    glm::vec4 point_normal = glm::normalize(normal);

    glm::vec4 half_vector = glm::normalize(light_dir + view_dir);
    float shininess = glm::pow(glm::dot(point_normal, half_vector),material->m_shininess);
    float NdotL = glm::max(glm::dot(point_normal,light_dir),0.0f);

    glm::vec3 light_diffuse =  glm::vec3(0.5,0.5,0.5);
    glm::vec3 light_specular = glm::vec3(0.5,0.5,0.5);
    glm::vec3 light_ambient = glm::vec3(0.2,0.2,0.2);

    glm::vec3 diffuse_color = material->m_diffuse*light_diffuse*NdotL;
    glm::vec3 specular_color = material->m_specular*light_specular*shininess;
    glm::vec3 ambient_color = material->m_ambient*light_ambient;
    glm::vec3 color = diffuse_color + specular_color + ambient_color;

    QColor pixel_color;
    pixel_color.setRgbF(color.x,color.y,color.z);
    return pixel_color;
}

//Stub for cast_ray function
QColor RayCaster::cast_ray(Ray ray, QList<QSharedPointer<CSGNode> > *nodes)
{      
    QList<QSharedPointer<CSGNode> >::iterator iter;
    for(iter = (nodes[0].end()-1); iter >= nodes[0].begin(); iter--){
        if((*iter)->m_visible){
            const std::list<CSGIntersection> intersections = (*iter)->set_membership( ray );
            std::list<CSGIntersection>::const_iterator int_iter;
            float t = FLT_MAX;
            glm::vec4 normal;
            CSGPrimitive *primitive = NULL;
            for ( int_iter = intersections.begin(); int_iter != intersections.end(); int_iter++ ) {
                if ( int_iter->t[0] < t && int_iter->t[0] >= 0) {
                    t = int_iter->t[0];
                    normal = int_iter->normal[0];
                    primitive = int_iter->primitive[0];
                }
            }
            if ( t != FLT_MAX ) {
                glm::vec4 temp = m_light_pos;
                glm::vec4 temp_obs = m_eye_world_pos;
                m_eye_world_pos = primitive->get_accumulated_inverse_matrix() * m_eye_world_pos;
                m_light_pos = primitive->get_accumulated_inverse_matrix() * m_light_pos;
                QColor res = phong_lighting(ray.m_origin + t*ray.m_direction, normal,primitive->m_material);
                m_light_pos = temp;
                m_eye_world_pos = temp_obs;
                return res;
            }
        }
    }
    return m_bgcolor;
}


QColor RayCaster::get_point_color(float x, float y, QList<QSharedPointer<CSGNode> > *nodes){
    if(nodes == NULL){
        return m_bgcolor;
    }
    if(nodes->size() > 0){
        glm::vec4 grid_pos = glm::vec4(x, y, 0, 1.0);
        Ray curr_ray;
        curr_ray.m_direction = m_result_orientation * glm::normalize(grid_pos - m_grid_eye_pos);
        curr_ray.m_origin = m_eye_world_pos;

        return cast_ray(curr_ray, nodes);
    }
    return m_bgcolor;
}

void RayCaster::rotate_x(float angle)
{
    //	rot_angle_x += angle;
    //	rot_angle_x = rot_angle_x % 360;
    glm::mat4 rotation = glm::gtx::transform::rotate( (float)angle, local_x.x, local_x.y, local_x.z );
    m_result_orientation = rotation * m_result_orientation;
    local_y = rotation *local_y;
    local_z = rotation *local_z;
}

void RayCaster::rotate_y(float angle)
{
    //	rot_angle_y += angle;
    //	rot_angle_y = rot_angle_y % 360;
    glm::mat4 rotation = glm::gtx::transform::rotate( (float)angle, local_y.x, local_y.y, local_y.z );
    m_result_orientation = rotation * m_result_orientation;
    local_x = rotation *local_x;
    local_z = rotation *local_z;
}

void RayCaster::rotate_z(float angle)
{
    //	rot_angle_z += angle;
    //	rot_angle_z = rot_angle_z % 360;
    glm::mat4 rotation = glm::gtx::transform::rotate( (float)angle, local_z.x, local_z.y, local_z.z );
    m_result_orientation = rotation * m_result_orientation;
    local_x = rotation *local_x;
    local_y = rotation *local_y;
}

void RayCaster::translate_x(float x)
{
    m_eye_world_pos = m_eye_world_pos + x*local_x;
}

void RayCaster::translate_y(float y)
{
    m_eye_world_pos = m_eye_world_pos + y*local_y;
}

void RayCaster::translate_z(float z)
{
    m_eye_world_pos = m_eye_world_pos + z*local_z;
}

void RayCaster::move(glm::vec3 move_dir)
{
    translate_x(move_dir.x);
    translate_y(move_dir.y);
    translate_z(move_dir.z);
}


void RayCaster::rotate(glm::vec3 rotate_vec, float angle)
{
    if(rotate_vec.x == 1){
        rotate_x(angle);
    }
    if(rotate_vec.y == 1){
        rotate_y(angle);
    }
    if(rotate_vec.z == 1){
        rotate_z(angle);
    }
}
